﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Security.Cryptography;
using System.IO;
using System.Management;
using Microsoft.Win32;


namespace SecurityTest
{
    public class RSA_API
    {
        static int RSA_BYTE_LIMIT = 117;
        static int RSA_BYTE_NUM = 128;

        public RSA_API()
        {
            try
            {
                //Create a UnicodeEncoder to convert between byte array and string.
                UnicodeEncoding ByteConverter = new UnicodeEncoding();

                //Create byte arrays to hold original, encrypted, and decrypted data.
                byte[] dataToEncrypt = ByteConverter.GetBytes("sdfghjk");
                byte[] encryptedData;
                byte[] decryptedData;

                Console.WriteLine("RSA origin length " + dataToEncrypt.Length);
                //Create a new instance of RSACryptoServiceProvider to generate
                //public and private key data.
                RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
                Console.WriteLine("key size " + RSA.KeySize);
                //Pass the data to ENCRYPT, the public key information 
                //(using RSACryptoServiceProvider.ExportParameters(false),
                //and a boolean flag specifying no OAEP padding.
                RSAParameters decrypt = RSA.ExportParameters(true);

                byte[] oringByte = RSA.ExportParameters(false).Modulus;
                string keyString = ByteToString(oringByte);

                encryptedData = RSAEncrypt(dataToEncrypt, keyString, false);


                
                //Pass the data to DECRYPT, the private key information 
                //(using RSACryptoServiceProvider.ExportParameters(true),
                //and a boolean flag specifying no OAEP padding.

                decryptedData = RSADecrypt(encryptedData, RSA.ExportParameters(true), false);
                RSAParameters parameters = RSA.ExportParameters(true);
                //Display the decrypted plaintext to the console. 
                Console.WriteLine("RSA origin length " + dataToEncrypt.Length);
                Console.WriteLine("RSA encrypt " + encryptedData.Length);
                Console.WriteLine("count length " + MeasureByteNumber(dataToEncrypt.Length));
                Console.WriteLine("Decrypted plaintext: {0}", ByteConverter.GetString(decryptedData));
                Console.WriteLine("RSA decrypt " + decryptedData.Length);
            }
            catch (ArgumentNullException)
            {
                //Catch this exception in case the encryption did
                //not succeed.
                Console.WriteLine("Encryption failed.");

            }
        }

        public static byte[] RSAEncrypt(byte[] DataToEncrypt, String array, bool DoOAEPPadding)
        {
            RSAParameters encryptParameter = new RSAParameters();
            byte[] exponent = new byte[] { 0x01, 0x00, 0x01 };
            encryptParameter.Exponent = exponent;

            encryptParameter.Modulus = StringToByte(array);

            return RSAEncrypt(DataToEncrypt, encryptParameter, DoOAEPPadding);
        }

        public static byte[] RSAEncrypt(byte[] DataToEncrypt, RSAParameters RSAKeyInfo, bool DoOAEPPadding)
        {
            try
            {
                //Create a new instance of RSACryptoServiceProvider.
                RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();

                //Import the RSA Key information. This only needs
                //toinclude the public key information.
                RSA.ImportParameters(RSAKeyInfo);

                //Encrypt the passed byte array and specify OAEP padding.  
                //OAEP padding is only available on Microsoft Windows XP or
                //later.  

                int cut = (int)Math.Floor(DataToEncrypt.Length / (float)RSA_BYTE_LIMIT);
                byte[] encryptData = new byte[(cut+1) * RSA_BYTE_NUM];

                //byte[] originEncrypt = RSA.Encrypt(DataToEncrypt, DoOAEPPadding);

                for (int i = 0; i < cut; i++)
                {
                    byte[] tempData = new byte[RSA_BYTE_LIMIT];
                    Array.Copy(DataToEncrypt, i * RSA_BYTE_LIMIT, tempData, 0, RSA_BYTE_LIMIT);

                    byte[] tempencryptData = RSA.Encrypt(tempData, DoOAEPPadding);
                    Array.Copy(tempencryptData, 0, encryptData, i * RSA_BYTE_NUM, RSA_BYTE_NUM);
                }

                int remainByteNum = DataToEncrypt.Length - RSA_BYTE_LIMIT * cut;

                byte[] tempRemainData = new byte[remainByteNum];
                Array.Copy(DataToEncrypt, RSA_BYTE_LIMIT * cut, tempRemainData, 0, remainByteNum);
                byte[] remainEncryptData = RSA.Encrypt(tempRemainData, DoOAEPPadding);
                Array.Copy(remainEncryptData, 0, encryptData, RSA_BYTE_NUM * cut, RSA_BYTE_NUM);


                return encryptData; 
            }
            //Catch and display a CryptographicException  
            //to the console.
            catch (CryptographicException e)
            {
                Console.WriteLine(e.Message);

                return null;
            }

        }

        static public byte[] RSADecrypt(byte[] DataToDecrypt, RSAParameters RSAKeyInfo, bool DoOAEPPadding)
        {
            try
            {
                //Create a new instance of RSACryptoServiceProvider.
                RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();

                //Import the RSA Key information. This needs
                //to include the private key information.
                RSA.ImportParameters(RSAKeyInfo);

                int cut = (int)Math.Floor(DataToDecrypt.Length / (float)RSA_BYTE_NUM) + 1;

                byte[] decryptData = new byte[0];
                for (int i = 1; i < cut; i++)
                {
                    byte[] tempData = new byte[RSA_BYTE_NUM];
                    Array.Copy(DataToDecrypt, (i - 1) * RSA_BYTE_NUM, tempData, 0, RSA_BYTE_NUM);
                    byte[] tempDecryptData = RSA.Decrypt(tempData, DoOAEPPadding);
                    decryptData = ByteConnect(decryptData, tempDecryptData);
                }
                //Decrypt the passed byte array and specify OAEP padding.  
                //OAEP padding is only available on Microsoft Windows XP or
                //later.  
                return decryptData;
            }
            //Catch and display a CryptographicException  
            //to the console.
            catch (CryptographicException e)
            {
                Console.WriteLine(e.ToString());

                return null;
            }

        }

        private static byte[] ByteConnect(byte[] first, byte[] second)
        {
            List<byte> connect = new List<byte>();
            connect.AddRange(first);
            connect.AddRange(second);
            return connect.ToArray();
        }

        public static int MeasureByteNumber(int originLength)
        {
            int realNum = (int)Math.Ceiling(originLength / 117.0f) * RSA_BYTE_NUM;
            if (originLength % 117 == 0)
            {
                realNum += RSA_BYTE_NUM;
            }

            return realNum;
        }


        static public String ByteToString(Byte[] array) 
        {
            String all ="";

            foreach (Byte b in array)
            {
                all += b.ToString()+",";
            }

            return all;
        }

        static public byte[] StringToByte(String str) 
        {
            List<Byte> byteArray = new List<Byte>();
            String[] data = str.Split(",".ToCharArray());

            foreach (String s in data) 
            {
                if (s.Trim().Length > 0) 
                {
                    byteArray.Add(Convert.ToByte(s));
                }
            }

            return byteArray.ToArray();
        }

    }

    public class AES_API
    {

        public byte[] AESEncoder(byte[] source, string key)  //AES 加密函數
        {
            byte[] bkey = new byte[32];
            byte[] iv = new byte[16];
            byte[] shaBuffer = new SHA384Managed().ComputeHash(Encoding.UTF8.GetBytes(key));
            RijndaelManaged aes = new RijndaelManaged();

            Array.Copy(shaBuffer, 0, bkey, 0, 32);
            Array.Copy(shaBuffer, 32, iv, 0, 16);

            ICryptoTransform ict = aes.CreateEncryptor(bkey, iv);
            MemoryStream ms = new MemoryStream();
            CryptoStream cs = new CryptoStream(ms, ict, CryptoStreamMode.Write);
            byte[] rawData = source;

            cs.Write(rawData, 0, rawData.Length);
            cs.Close();
            return ms.ToArray();
        }

        public byte[] AESDecoder(byte[] source, string key)  //AES 解密函數
        {
            byte[] bkey = new byte[32];
            byte[] iv = new byte[16];
            byte[] shaBuffer = new SHA384Managed().ComputeHash(Encoding.UTF8.GetBytes(key));
            Console.WriteLine(shaBuffer.Length);
            RijndaelManaged aes = new RijndaelManaged();

            Array.Copy(shaBuffer, 0, bkey, 0, 32);
            Array.Copy(shaBuffer, 32, iv, 0, 16);

            ICryptoTransform ict = aes.CreateDecryptor(bkey, iv);
            MemoryStream ms = new MemoryStream();
            CryptoStream cs = new CryptoStream(ms, ict, CryptoStreamMode.Write);
            byte[] rawData = source;
            cs.Write(rawData, 0, rawData.Length);
            cs.Close();
            return ms.ToArray();
        }




        private void desEncryptFile(string sourceFile, string encryptFile)
        {
            if (string.IsNullOrEmpty(sourceFile) || string.IsNullOrEmpty(encryptFile))
            {
                return;
            }
            if (!File.Exists(sourceFile))
            {
                return;
            }

            DESCryptoServiceProvider des = new DESCryptoServiceProvider();
            byte[] key = Encoding.ASCII.GetBytes("12345678");
            byte[] iv = Encoding.ASCII.GetBytes("87654321");

            des.Key = key;
            des.IV = iv;

            using (FileStream sourceStream = new FileStream(sourceFile, FileMode.Open, FileAccess.Read))
            using (FileStream encryptStream = new FileStream(encryptFile, FileMode.Create, FileAccess.Write))
            {
                //檔案加密
                byte[] dataByteArray = new byte[sourceStream.Length];
                sourceStream.Read(dataByteArray, 0, dataByteArray.Length);

                using (CryptoStream cs = new CryptoStream(encryptStream, des.CreateEncryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(dataByteArray, 0, dataByteArray.Length);
                    cs.FlushFinalBlock();
                }
            }
        }
        private void desDecryptFile(string encryptFile, string decryptFile)
        {
            if (string.IsNullOrEmpty(encryptFile) || string.IsNullOrEmpty(decryptFile))
            {
                return;
            }
            if (!File.Exists(encryptFile))
            {
                return;
            }

            DESCryptoServiceProvider des = new DESCryptoServiceProvider();
            byte[] key = Encoding.ASCII.GetBytes("12345678");
            byte[] iv = Encoding.ASCII.GetBytes("87654321");

            des.Key = key;
            des.IV = iv;

            using (FileStream encryptStream = new FileStream(encryptFile, FileMode.Open, FileAccess.Read))
            using (FileStream decryptStream = new FileStream(decryptFile, FileMode.Create, FileAccess.Write))
            {
                byte[] dataByteArray = new byte[encryptStream.Length];
                encryptStream.Read(dataByteArray, 0, dataByteArray.Length);
                using (CryptoStream cs = new CryptoStream(decryptStream, des.CreateDecryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(dataByteArray, 0, dataByteArray.Length);
                    cs.FlushFinalBlock();
                }
            }
        }
    }
}
